import fs from 'fs-extra'
import path from 'path'
import { mergeWith } from 'lodash-es'
import { pathToFileURL } from 'url'
import { sortDependencies } from './sortDependencies'

// 注意在进行merge的时候，数组的merge需要自定义一下，否则数组会覆盖前面数组的内容
const mergeJsonCustomize = (objValue: any, srcValue: any) => {
  if (Array.isArray(objValue)) {
    return objValue.concat(srcValue)
  }
}

// 针对正常的json文件的处理，合并json对象然后进行写入文件即可
const writeMergeJson = (src: string, dest: string) => {
  const oldJson = JSON.parse(fs.readFileSync(dest, { encoding: 'utf-8' }))
  const newJson = JSON.parse(fs.readFileSync(src, { encoding: 'utf-8' }))
  const json = mergeWith({}, oldJson, newJson, mergeJsonCustomize)
  fs.writeFileSync(dest, JSON.stringify(json, null, 2) + '\n')
}

// 针对package.json文件的处理，合并需要对依赖字段进行排序处理
const writeMergePackageJson = (src: string, dest: string) => {
  const oldJson = JSON.parse(fs.readFileSync(dest, { encoding: 'utf-8' }))
  const newJson = JSON.parse(fs.readFileSync(src, { encoding: 'utf-8' }))
  const json = sortDependencies(mergeWith({}, oldJson, newJson, mergeJsonCustomize))
  fs.writeFileSync(dest, JSON.stringify(json, null, 2) + '\n')
}

// 直接追加到文件末尾
const writeAppendFile = (src: string, dest: string) => {
  const oldFile = fs.readFileSync(dest, { encoding: 'utf-8' })
  const newFile = fs.readFileSync(src, { encoding: 'utf-8' })
  fs.writeFileSync(dest, oldFile + '\n' + newFile)
}

export const renderTemplate = (src: string, dest: string, callbacks) => {
  const stats = fs.statSync(src)

  /**
   * 对目录进行操作
   */
  if (stats.isDirectory()) {
    // 忽略node_modules
    if (path.basename(src) === 'node_modules') {
      return
    }
    // 如果目录已经存在，默认会抛出错误（除非设置了 { recursive: true }）
    fs.mkdirSync(dest, { recursive: true })
    // 渲染子目录和文件
    for (const file of fs.readdirSync(src)) {
      renderTemplate(path.resolve(src, file), path.resolve(dest, file), callbacks)
    }
    return
  }

  /**
   * 后面就是针对文件的操作
   */

  const filename = path.basename(src) // 文件名
  const isExistFile = fs.existsSync(dest) // 是否存在文件

  // package.json
  if (filename === 'package.json' && isExistFile) {
    writeMergePackageJson(src, dest)
    return
  }

  // extensions.json
  if (filename === 'extensions.json' && isExistFile) {
    writeMergeJson(src, dest)
    return
  }

  // settings.json
  if (filename === 'settings.json' && isExistFile) {
    writeMergeJson(src, dest)
    return
  }

  // _开头的文件名字，复制到目录视为.开头的文件名字，因为很多.开头的配置文件会影响到其他文件，所以模板中使用_开头的规范
  if (filename.startsWith('_')) {
    dest = path.resolve(path.dirname(dest), filename.replace(/^_/, '.'))
  }

  // 处理.gitignore文件，追加内容到文件末尾
  if (filename === '_gitignore' && isExistFile) {
    writeAppendFile(src, dest)
    return
  }

  // 针对xxx.ejs，对应模板数据是xxx.data.mjs
  // xxx.data.mjs导出getData方法来生成对应的模板数据
  // 针对xxx.ejs可以有多个getData方法调用，后面的getData方法会得到之前的getData的数据传参
  if (filename.endsWith('.data.mjs')) {
    // 去除后缀就表示是对应的dest
    dest = dest.replace(/\.data\.mjs$/, '')
    // 记录函数数组，后续使用
    callbacks.push(async (dataStore) => {
      const getData = (await import(pathToFileURL(src).toString())).default
      dataStore[dest] = await getData({
        oldData: dataStore[dest] || {},
      })
    })
    return
  }

  // 其他情况，直接复制文件，用后面的文件进行覆盖前面的文件内容
  fs.copyFileSync(src, dest)
}
